package com.tianji.promotion.service.impl;

import com.alibaba.nacos.client.naming.utils.CollectionUtils;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.tianji.common.domain.dto.PageDTO;
import com.tianji.common.exceptions.BizIllegalException;
import com.tianji.common.utils.*;
import com.tianji.promotion.constants.PromotionConstants;
import com.tianji.promotion.domain.dto.CouponFormDTO;
import com.tianji.promotion.domain.dto.CouponIssueFormDTO;
import com.tianji.promotion.domain.po.Coupon;
import com.tianji.promotion.domain.po.CouponScope;
import com.tianji.promotion.domain.po.UserCoupon;
import com.tianji.promotion.domain.query.CouponQuery;
import com.tianji.promotion.domain.vo.CouponPageVO;
import com.tianji.promotion.domain.vo.CouponVO;
import com.tianji.promotion.enums.CouponStatus;
import com.tianji.promotion.enums.ObtainType;
import com.tianji.promotion.mapper.CouponMapper;
import com.tianji.promotion.service.ICouponScopeService;
import com.tianji.promotion.service.ICouponService;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.tianji.promotion.service.IExchangeCodeService;
import com.tianji.promotion.service.IUserCouponService;
import lombok.RequiredArgsConstructor;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Service;

import java.time.LocalDateTime;
import java.util.*;
import java.util.stream.Collectors;

/**
 * <p>
 * 优惠券的规则信息 服务实现类
 * </p>
 *
 * @author hrz
 * @since 2025-09-01
 */
@Service
@RequiredArgsConstructor
public class CouponServiceImpl extends ServiceImpl<CouponMapper, Coupon> implements ICouponService {

    private final ICouponScopeService couponScopeService;
    private final IExchangeCodeService exchangeCodeService;
    private final IUserCouponService userCouponService;
    private final RedisTemplate redisTemplate;

    @Override
    public void addNewCoupon(CouponFormDTO couponFormDTO) {
        //1.DTO转PO保存到优惠券数据表中
        Coupon coupon = BeanUtils.copyBean(couponFormDTO, Coupon.class);
        coupon.setType(1);
        coupon.setStatus(CouponStatus.DRAFT.getValue());
        coupon.setCreateTime(LocalDateTime.now());
        this.save(coupon); //保存优惠券到数据库
        //2.判断是否是限定范围的优惠券
        if(!coupon.getSpecific()){
            //不是直接返回
            return;
        }
        //3.是的就再判断是否传递了范围数组
        if(CollUtils.isEmpty(couponFormDTO.getScopes())){
            //没有传递范围数组，直接返回
            throw new BizIllegalException("数据非法,未传递范围");
        }
        //保存范围
        Long id = coupon.getId(); //优惠券id
        List<CouponScope> collect = couponFormDTO.getScopes().stream()
                .map(param -> new CouponScope().setCouponId(id).setType(1).setBizId(param))
                .collect(Collectors.toList());
        //批量保存起来
        couponScopeService.saveBatch(collect);
    }

    @Override
    public PageDTO<CouponPageVO> queryCouponPage(CouponQuery query) {
        //1.查询
        Page<Coupon> page = this.lambdaQuery()
                .eq(query.getType() != null, Coupon::getDiscountType, query.getType())
                .eq(query.getStatus() != null, Coupon::getStatus, query.getStatus())
                .like(StringUtils.isNotBlank(query.getName()), Coupon::getName, query.getName())
                .page(query.toMpPageDefaultSortByCreateTimeDesc());
        //2.预备好结果集合
        ArrayList<CouponPageVO> list = new ArrayList<>();
        List<Coupon> records = page.getRecords();
        //3.判断是否有数据
        if(CollUtils.isEmpty(records)){
            //没有数据，直接返回
            return PageDTO.empty(page);
        }
        //3.遍历
        for (Coupon record : records) {
            CouponPageVO vo = BeanUtils.copyBean(record, CouponPageVO.class);
            list.add(vo);
        }
        //4.返回
        return PageDTO.of(page, list);
    }

    //发放优惠券
    @Override
    public void issueCoupon(Long id, CouponIssueFormDTO couponIssueDTO) {
        //1.查询优惠券
        Coupon coupon = this.getById(id);
        Coupon copy = coupon;
        //2.判断优惠券是否存在
        if(coupon == null){
            throw new BizIllegalException("优惠券不存在");
        }
        //3.如果存在就判断一下优惠券的状态
        if(coupon.getStatus() != CouponStatus.DRAFT.getValue()
                && coupon.getStatus()!=CouponStatus.PAUSE.getValue()){
            throw new BizIllegalException("优惠券状态异常");
        }
        //4.判断发放时间
        if(couponIssueDTO.getIssueEndTime() == null
                || couponIssueDTO.getIssueEndTime().isBefore(LocalDateTime.now())
                || couponIssueDTO.getIssueBeginTime().isAfter(couponIssueDTO.getIssueEndTime())){
            throw new BizIllegalException("数据非法");
        }
        //5.判断发放的方式
        boolean isBeginNow = (!couponIssueDTO.getIssueBeginTime().isAfter(LocalDateTime.now()))
                || couponIssueDTO.getIssueBeginTime() == null;
        if(isBeginNow){
            //立刻发放
            coupon.setStatus(CouponStatus.ISSUING.getValue());
            couponIssueDTO.setIssueBeginTime(LocalDateTime.now());
        }else{
            coupon.setStatus(CouponStatus.UN_ISSUE.getValue());
        }
        //6.设置发放时间
        coupon.setIssueBeginTime(couponIssueDTO.getIssueBeginTime());
        coupon.setIssueEndTime(couponIssueDTO.getIssueEndTime());
        //7.设置使用的有效期或者天数 - 直接设置就可以,如果没传就为空
        coupon.setTermDays(couponIssueDTO.getTermDays());
        coupon.setTermBeginTime(couponIssueDTO.getTermBeginTime());
        coupon.setTermEndTime(couponIssueDTO.getTermEndTime());
        //8.更新到数据库
        this.updateById(coupon);
        //9.如果是兑换码的形式
        copy.setIssueEndTime(couponIssueDTO.getIssueEndTime());
        if(copy.getStatus() == CouponStatus.DRAFT.getValue()
                && copy.getObtainWay() == ObtainType.ISSUE){
            //10.异步生成兑换码 - 如果是暂停状态的就无需生成
            exchangeCodeService.generateCode(copy);
        }
        //11.发放优惠券时将优惠券信息页保存到redis中
        if(isBeginNow){
            HashMap<String,String> map = new HashMap<>(4);
            map.put("issueBeginTime", String.valueOf(DateUtils.toEpochMilli(coupon.getIssueBeginTime())));
            map.put("issueEndTime", String.valueOf(DateUtils.toEpochMilli(coupon.getIssueEndTime())));
            map.put("totalNum", String.valueOf(coupon.getTotalNum()));
            map.put("userLimit", String.valueOf(coupon.getUserLimit()));
            redisTemplate.opsForHash().putAll(
                    PromotionConstants.COUPON_CACHE_KEY_PREFIX+id,map);
        }
    }

    @Override
    public List<CouponVO> queryCouponIssuing() {
        //1.先去数据库查询出所有处于发放中的优惠券,且是手动领取的
        List<Coupon> list = this.lambdaQuery()
                .eq(Coupon::getStatus, CouponStatus.ISSUING.getValue())
                .eq(Coupon::getObtainWay, ObtainType.PUBLIC)
                .list();
        Set<Long> collect = list.stream().map(Coupon::getId).collect(Collectors.toSet());
        //2.查询当前用户所有优惠券的已领取数量
        //先查询所有正在发放的优惠券里面,该用户已经领取过的优惠券
        List<UserCoupon> list1 = userCouponService.lambdaQuery()
                .eq(UserCoupon::getUserId, UserContext.getUser())
                .in(UserCoupon::getCouponId, collect)
                .list();
        Map<Long, Long> GotMap = list1
                .stream()
                .collect(Collectors.groupingBy(UserCoupon::getCouponId
                        , Collectors.counting()));
        //3.查询当前用户已领取的优惠券且未使用的数量
        Map<Long, Long> UnUseCount = list1
                .stream()
                .filter(l -> l.getStatus() == 1)
                .collect(
                Collectors.groupingBy(UserCoupon::getCouponId, Collectors.counting()));
        //封装VO
        List<CouponVO> voList = new ArrayList<>();
        for (Coupon coupon : list) {
            CouponVO vo = BeanUtils.copyBean(coupon, CouponVO.class);
            boolean available = coupon.getIssueNum() < coupon.getTotalNum()
                    && GotMap.getOrDefault(coupon.getId(),0L) < coupon.getUserLimit();
            vo.setAvailable(available); //是否可获得
            boolean received = UnUseCount.get(coupon.getId()) > 0;
            vo.setReceived(received); //是否可使用
            voList.add(vo);
        }
        return voList;
    }

    @Override
    public void updateCoupon(Long id, CouponFormDTO couponFormDTO) {
        //1.查询优惠券
        Coupon byId = this.getById(id);
        if(byId == null){
            throw new BizIllegalException("优惠券不存在");
        }
        if(byId.getStatus() != CouponStatus.DRAFT.getValue()){
            throw new BizIllegalException("非待发放状态! 不可修改!");
        }
        //2.修改字段
        BeanUtils.copyProperties(couponFormDTO, byId);
        //3.更新到数据库中
        this.updateById(byId);
    }

    @Override
    public void deleteCoupon(Long id) {
        //1.查询优惠券
        Coupon coupon = this.getById(id);
        //2.判断优惠券是否存在
        if(coupon == null){
            throw new BizIllegalException("优惠券不存在");
        }
        //3.判断优惠券的状态
        if(coupon.getStatus() != CouponStatus.DRAFT.getValue()){
            throw new BizIllegalException("优非待发放状态! 不可删除!");
        }
        //4.删除优惠券
        this.removeById(id);
    }

    @Override
    public void pauseCoupon(Long CouponId) {
        //1.查询优惠券
        Coupon coupon = this.getById(CouponId);
        //2.判断优惠券是否存在
        if(coupon == null){
            throw new BizIllegalException("优惠券不存在");
        }
        //3.判断优惠券的状态
        if(coupon.getStatus() != CouponStatus.ISSUING.getValue()
                && coupon.getStatus() != CouponStatus.UN_ISSUE.getValue()){
            throw new BizIllegalException("优惠券状态错误! 不可暂停!");
        }
        //4.暂停发放
        coupon.setStatus(CouponStatus.PAUSE.getValue());
        this.updateById(coupon);

        // 5.删除缓存
        redisTemplate.delete(PromotionConstants.COUPON_CACHE_KEY_PREFIX + CouponId);
    }
}
